﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tilver.Coder.Utility;
using Tilver.Coder.Core;
using System.Data;

namespace Tilver.Coder.Engine
{
    internal class EOEngine<T> : EngineBase
        where T : IEOGenerable
    {
        /// <summary>
        /// 实体类
        /// </summary>
        public T EO { get; set; }
        /// <summary>
        /// 输出代码
        /// </summary>
        /// <returns></returns>
        public override string RenderToString()
        {
            var response = new StringBuilder();
            //生成类
            ResponseClass(response, Tabs);
            return response.ToString();
        }

        #region 输出类
        private void ResponseClass(StringBuilder response, int tabs)
        {
            response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, EO.Description, string.Format("{0}({1})", EO.FullName, "实体类")));
            response.AppendTabLine(tabs, "[Serializable]");
            response.AppendFormatLine(tabs, "public class {0} : IRowMapper<{0}>", EO.EOName);
            response.AppendTabLine(tabs, "{");
            //输出字段成员
            ResponseFields(response, tabs + 1);
            response.AppendLine();
            //输出属性
            ResponseProperties(response, tabs + 1);
            response.AppendLine();
            //输出构造函数
            ResponseCtors(response, tabs + 1);
            response.AppendLine();
            //输出实体映射方法
            ResponseMap(response, tabs + 1);
            response.AppendLine();
            //输出枚举方法
            ResponseEnums(response, tabs + 1);
            response.AppendTabLine(tabs, "}");
        }
        #endregion

        #region 输出字段成员
        protected virtual void ResponseFields(StringBuilder response, int tabs)
        {
            if (EO.HasPrimaryKey)
            {
                response.AppendTabLine(tabs, "protected bool _hasOriginal = false;");
            }
            foreach (var column in EO.Columns)
            {
                ResponseField(response, tabs, column, false);
                if (column.IsPrimaryKey)
                {
                    ResponseField(response, tabs, column, true);
                }
            }
        }
        private void ResponseField(StringBuilder response, int tabs, CoderColumn column, bool isPrimaryKey)
        {
            response.AppendFormatLine(tabs, "protected {0} {1};", column.CSharpType, isPrimaryKey ? column.CodeName.ToCoderFieldNameByPrimaryColumn() : column.CodeName.ToCoderFieldName());
        }
        #endregion

        #region 输出属性

        protected virtual void ResponseProperties(StringBuilder response, int tabs)
        {
            if (EO.HasPrimaryKey)
            {
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "主键是否有原始值（即数据库中保存的值），用于实体对象更新时"));
                response.AppendTabLine(tabs, "public bool HasOriginal");
                response.AppendTabLine(tabs, "{");
                response.AppendTabLine(tabs + 1, "get { return _hasOriginal; }");
                response.AppendTabLine(tabs, "}");
                response.AppendLine();
            }
            foreach (var column in EO.Columns)
            {
                ResponseProperty(response, tabs, column, false);
                if (column.IsPrimaryKey)
                {
                    ResponseProperty(response, tabs, column, true);
                }
            }
        }
        private void ResponseProperty(StringBuilder response, int tabs, CoderColumn column, bool isPrimaryKey)
        {
            string propertyName = isPrimaryKey ? column.CodeName.ToCoderPropertyNameByPrimaryColumn() : column.CodeName.ToCoderPropertyName();
            string memberName = isPrimaryKey ? column.CodeName.ToCoderFieldNameByPrimaryColumn() : column.CodeName.ToCoderFieldName();
            string summary = string.Empty;
            if (isPrimaryKey)
            {
                summary = ExtensionUtil.GetCoderSummary(tabs, column.Description, "数据库中的原始主键值副本");
            }
            else
            {
                string desc = string.Empty;
                if (column.IsPrimaryKey)
                {
                    desc = "主键";
                }
                else if (column.IsForeignKey)
                {
                    desc = "外键";
                }
                else
                {
                    desc = "字段";
                }
                summary = ExtensionUtil.GetCoderSummary(tabs, column.Description, string.Format("[{0}]  [{1}]{2}", desc, column.NativeType, (column.IsIdentity ? "   自增字段" : string.Empty)));
            }
            response.AppendLine();
            response.AppendLine(summary);
            response.AppendFormatLine(tabs, "public {0} {1}", column.CSharpType, propertyName);
            response.AppendTabLine(tabs, "{");
            response.AppendFormatLine(tabs + 1, "get {{ return {0}; }}", memberName);
            if (column.Size > 0 && column.DbType == DbType.String && column.NativeType != "text" && column.NativeType != "ntext")
            {
                response.AppendTabLine(tabs + 1, "set {");
                string limitStr = column.AllowDBNull ? "value != null && " : "value == null || ";
                response.AppendFormatLine(tabs + 2, "if ({0}value.Length > {1})", limitStr, column.Size);
                response.AppendFormatLine(tabs + 3, "throw new ArgumentOutOfRangeException(\"Invalid value for {0}\", value, value == null ? \"null\" : value.ToString());", propertyName);
                if (isPrimaryKey)
                {
                    response.AppendTabLine(tabs + 2, "_hasOriginal = true;");
                }
                response.AppendFormatLine(tabs + 2, "{0} = value;", memberName);
                response.AppendTabLine(tabs + 1, "}");
            }
            else
            {
                response.AppendTab(tabs + 1, "set { ");
                if (isPrimaryKey)
                {
                    response.AppendLine();
                    response.AppendTabLine(tabs + 2, "_hasOriginal = true;");
                    response.AppendTab(tabs + 2);
                }
                response.AppendFormat("{0} = value;", memberName);
                if (isPrimaryKey)
                {
                    response.AppendLine();
                    response.AppendTab(tabs + 1);
                }
                response.Append("}");
                response.AppendLine();
            }
            response.AppendLine(ExtensionUtil.Tab(tabs) + "}");
        }
        #endregion

        #region 输出构造函数

        private void ResponseCtors(StringBuilder response, int tabs)
        {
            //默认构造函数
            response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "默认构造函数"));
            response.AppendFormatLine(tabs, "public {0}() ", EO.EOName);
            response.AppendTabLine(tabs, "{");
            foreach (var column in EO.Columns)
            {
                var cSharpDefaultValue = DBTextStyle.GetCSharpDefaultValue(column);
                if (string.IsNullOrEmpty(cSharpDefaultValue)) { continue; }
                response.AppendFormatLine(tabs + 1, "{0} = {1};", column.CodeName.ToCoderPropertyName(), cSharpDefaultValue);
            }
            response.AppendTabLine(tabs, "}");
            response.AppendLine();

            //自定义构造函数
            response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "自定义构造函数"));
            response.AppendLine(ExtensionUtil.GetDataObjectsInfo(tabs, EO.Columns, ExtensionUtil.IndexType.ParamSummary));
            response.AppendFormatLine(tabs, "public {0}({1}) ", EO.EOName, ExtensionUtil.GetDataObjectsInfo(tabs, EO.Columns, ExtensionUtil.IndexType.ParamList));
            response.AppendTabLine(tabs, "{");

            foreach (var column in EO.Columns)
            {
                response.AppendFormatLine(tabs + 1, "{0} = {1};", column.CodeName.ToCoderFieldName(), column.CodeName.ToCoderParameterName());
            }
            response.AppendTabLine(tabs, "}");
        }

        private void ResponseSerializeFunction(StringBuilder response, int tabs)
        {
            response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "支持序列化的构造函数"));
            response.AppendFormatLine(tabs, "protected {0}(SerializationInfo info, StreamingContext context) ", EO.EOName);
            response.AppendTabLine(tabs, "{");
            if (EO.HasPrimaryKey)
            {
                response.AppendTabLine(tabs + 1, "_hasOriginal = (bool)info.GetValue(\"_hasOriginal\", typeof(bool));");
            }
            foreach (var column in EO.Columns)
            {
                response.AppendFormatLine(tabs + 1, "{0} = ({1})info.GetValue(\"{0}\", typeof({1}));", column.CodeName.ToCoderFieldName(), column.CSharpType);
                if (column.IsPrimaryKey)
                {
                    response.AppendFormatLine(tabs + 1, "{0} = ({1})info.GetValue(\"{0}\", typeof({1}));", column.CodeName.ToCoderFieldNameByPrimaryColumn(), column.CSharpType);
                }
            }
            response.AppendTabLine(tabs, "}");
            response.AppendLine();

            response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "支持序列化的方法"));
            response.AppendTabLine(tabs, "[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]");
            response.AppendTabLine(tabs, "public void GetObjectData(SerializationInfo info, StreamingContext context)");
            response.AppendTabLine(tabs, "{");

            if (EO.HasPrimaryKey)
            {
                response.AppendTabLine(tabs + 1, "info.AddValue(\"_hasOriginal\", _hasOriginal);");
            }
            foreach (var column in EO.Columns)
            {
                response.AppendFormatLine(tabs + 1, "info.AddValue(\"{0}\", {0});", column.CodeName.ToCoderFieldName());
                if (column.IsPrimaryKey)
                {
                    response.AppendFormatLine(tabs + 1, "info.AddValue(\"{0}\", {0});", column.CodeName.ToCoderFieldNameByPrimaryColumn());
                }
            }
            response.AppendTabLine(tabs, "}");
        }
        #endregion

        #region 实体映射结果集

        private void ResponseMap(StringBuilder response, int tabs)
        {
            //bool hasDeep = EO.Columns.Any(w => w.IsForeignKey);
            response.AppendLine();
            response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "将结果集映射成实体对象"));
            response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "reader", "只进结果集流"));
            response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
            response.AppendFormatLine(tabs, "public {0} MapRow(IDataReader reader)", EO.EOName);
            response.AppendTabLine(tabs, "{");
            response.AppendFormatLine(tabs + 1, "var ret = new {0}();", EO.EOName);
            int columnIndex = 0;
            ResponseMapColumns(response, tabs + 1, EO.Columns, ref columnIndex, string.Empty);
            ResponseMapOtherColumns(response, tabs + 1, ref columnIndex);
            response.AppendTabLine(tabs + 1, "return ret;");
            response.AppendTabLine(tabs, "}");

            response.AppendLine();
            response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "将结果集映射成实体对象，返回映射方法"));
            response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
            response.AppendFormatLine(tabs, "public Func<IDataReader, {0}> GetMapper()", EO.EOName);
            response.AppendTabLine(tabs, "{");
            response.AppendTabLine(tabs + 1, "return MapRow;");
            response.AppendTabLine(tabs, "}");
            response.AppendLine();

            /*
            //map
            response.AppendLine();
            response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "将结果集映射成实体对象"));
            response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "reader", "只进结果集流"));
            response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
            response.AppendFormatLine(tabs, "public {0} MapRow(IDataReader reader)", EO.EOName);
            response.AppendTabLine(tabs, "{");

            if (hasdeep)
                response.AppendTabLine(tabs + 1, "return GetItem(reader , false);");
            else
                response.AppendTabLine(tabs + 1, "return GetItem(reader);");
            response.AppendTabLine(tabs, "}");

            if (hasdeep)
            {
                response.AppendLine();
                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "将结果集映射成实体对象"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "reader", "只进结果集流"));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendFormatLine(tabs, "public {0} MapDeep(IDataReader reader)", EO.EOName);
                response.AppendTabLine(tabs, "{");
                response.AppendTabLine(tabs + 1, "return GetItem(reader , true);");
                response.AppendTabLine(tabs, "}");

                response.AppendLine();

                response.AppendLine(ExtensionUtil.GetCoderSummary(tabs, "根据是否深加载，返回映射方法"));
                response.AppendLine(ExtensionUtil.GetCoderParamSummary(tabs, "deep", "是否深加载"));
                response.AppendLine(ExtensionUtil.GetCoderReturnSummary(tabs, string.Empty));
                response.AppendFormatLine(tabs, "public Func<IDataReader, {0}> GetMapper(bool deep)", EO.EOName);
                response.AppendTabLine(tabs, "{");
                response.AppendTabLine(tabs + 1, "if (deep)");
                response.AppendTabLine(tabs + 2, "return MapDeep;");
                response.AppendTabLine(tabs + 1, "else");
                response.AppendTabLine(tabs + 2, "return MapRow;");
                response.AppendTabLine(tabs, "}");
            }

            response.AppendLine();

            if (hasdeep)
                response.AppendFormatLine(tabs, "private {0} GetItem(IDataReader reader, bool deep)", EO.EOName);
            else
                response.AppendFormatLine(tabs, "private static {0} GetItem(IDataReader reader)", EO.EOName);
            response.AppendTabLine(tabs, "{");
            response.AppendFormatLine(tabs + 1, "{0} ret = new {0}();", EO.EOName);
            response.AppendLine();

            int columnIndex = 0;
            ResponseMapColumns(response, tabs + 1, EO.Columns, ref columnIndex, string.Empty);
            ResponseMapOtherColumns(response, tabs + 1, ref columnIndex);
            response.AppendTabLine(tabs + 1, "return ret;");
            response.AppendTabLine(tabs, "}");
             * */
        }

        protected virtual void ResponseMapOtherColumns(StringBuilder response, int tabs, ref int columnIndex)
        {
        }
        /// <summary>
        /// 输出字段代码
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        /// <param name="columns"></param>
        /// <param name="columnIndex"></param>
        /// <param name="objectName"></param>
        protected void ResponseMapColumns(StringBuilder response, int tabs, IEnumerable<CoderColumn> columns, ref  int columnIndex, string objectName)
        {
            foreach (var column in columns)
            {
                string propertyName = column.CodeName.ToCoderPropertyName();
                string paramName = string.Format("\"{0}\"", column.Name);

                if (!string.IsNullOrEmpty(objectName))
                    propertyName = objectName + "." + propertyName;

                if (column.AllowDBNull)
                {
                    if (column.IsEnumType)
                    {
                        response.AppendLine();
                        response.AppendFormatLine(tabs, "if (reader[{0}] is DBNull)", paramName);
                        response.AppendFormatLine(tabs + 1, "ret.{0} = null;", propertyName);
                        response.AppendTabLine(tabs, "else");
                        response.AppendFormatLine(tabs + 1, "ret.{0} = ({1})reader[{2}];", propertyName, column.CSharpType, paramName);
                    }
                    else
                    {
                        response.AppendFormatLine(tabs, "ret.{0} = (reader[{2}] is DBNull) ? null : ({1})reader[{2}];", propertyName, column.CSharpType, paramName);
                        //if (column.CSharpType == "string")
                        //    response.AppendFormatLine(tabs, "ret.{0} = (reader[{2}] is DBNull) ? null : ({1})reader[{2}];", propertyName, column.CSharpType, paramName);
                        //else
                        //    response.AppendFormatLine(tabs, "ret.{0} = (reader[{2}] is DBNull) ? ({1})null : {3}.Parse(reader[{2}].ToString());", propertyName, column.CSharpType, paramName, column.CSharpType);
                    }
                }
                else
                {
                    response.AppendFormatLine(tabs, "ret.{0} = ({1})reader[{2}];", propertyName, column.CSharpType, paramName);
                    //if (column.CSharpType == "string" || column.IsEnumType)
                    //    response.AppendFormatLine(tabs, "ret.{0} = ({1})reader[{2}];", propertyName, column.CSharpType, paramName);
                    //else
                    //    response.AppendFormatLine(tabs, "ret.{0} = {1}.Parse(reader[{2}].ToString());", propertyName, column.CSharpType, paramName);
                }
                if (column.IsPrimaryKey)
                {
                    var propertyNameForPrimaryColumn = column.CodeName.ToCoderPropertyNameByPrimaryColumn();
                    if (!string.IsNullOrEmpty(objectName))
                        propertyNameForPrimaryColumn = objectName + "." + propertyNameForPrimaryColumn;
                    response.AppendFormatLine(tabs, "ret.{0} = ret.{1};", propertyNameForPrimaryColumn, propertyName);
                }
                columnIndex++;
            }
        }
        #endregion

        #region 输出枚举类
        /// <summary>
        /// 输出枚举代码
        /// </summary>
        /// <param name="response"></param>
        /// <param name="tabs"></param>
        private void ResponseEnums(StringBuilder response, int tabs)
        {
            var enumEngine = new EnumEngine { DBTextStyle = DBTextStyle, Tabs = tabs, NameSpace = string.Empty };

            foreach (var column in EO.Columns.Where(w => w.IsEnumType && w.AllowGenerateEnum))
            {
                enumEngine.Enum = CoderEnum.Parse(column);
                response.AppendLine(enumEngine.RenderToString());
            }
        }
        #endregion
    }
}
